/**
 * @infor 日期工具
 * @path  @/utils/dateUtils.js
 * @user  xsq
 * @date  2019-09-17 14:25:56
 */

// 日期格式正则
const C_TOKEN = /d{1,4}|M{1,4}|yy(?:yy)?|S{1,3}|Do|ZZ|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g
// 两位数位正则
const C_TWO_DIGITS = /\d\d?/
// 三位数位正则
// const C_THREE_DIGITS = /\d{3}/
// 四位数位正则
const C_FOUR_DIGITS = /\d{4}/

const C_PARSE_FLAGS = {
  yyyy: [C_FOUR_DIGITS, (data, value) => (data.year = value)],
  MM: [C_TWO_DIGITS, (data, value) => (data.month = value - 1)],
  dd: [C_TWO_DIGITS, (data, value) => (data.day = value)],
  hh: [C_TWO_DIGITS, (data, value) => (data.hour = value)],
  mm: [C_TWO_DIGITS, (data, value) => (data.minute = value)],
  ss: [C_TWO_DIGITS, (data, value) => (data.second = value)]
}

/**
 * 格式化日期
 * @param  {Any}    date 字符串形式日期
 * @param  {String} mart 格式化格式
 * @return {String}        转化后的日期对象
 */
export const formart = (date, mart) => {
  if (!date) {
    return ''
  }
  if (typeof date === 'string') {
    const dateObj = parseDate(date)
    return formatDate(dateObj, mart)
  }
  return formatDate(date, mart)
}

/**
 * 字符串转日期对象
 * @param  {String} dateString 字符串形式日期
 * @param  {String} format     格式化格式
 * @return {Date}              转化后的日期对象
 */
export const parseDate = (dateString, format = 'yyyy-MM-dd hh:mm:ss') => {
  // 非确定性有穷自动机（NFA）在字符串长度增加时需要决策的长度会呈指数级增长
  // 优先过滤长字符串，排除非日期
  if (dateString.length > 1000) return

  // 传入是否是合法字符串日期
  let isValid = true
  const dateInfo = {}
  /* let form = */format.replace(C_TOKEN, $0 => {
    if (C_PARSE_FLAGS[$0]) {
      const info = C_PARSE_FLAGS[$0]
      const index = dateString.search(info[0])
      // 没有检索到字符，设置为非法字符
      if (!~index) {
        isValid = false
      } else {
        // 通过 replace 方法判断正则
        dateString.replace(info[0], result => {
          // 组装 dateInfo 对象
          info[1](dateInfo, result)
          dateString = dateString.substr(index + result.length)
          return result
        })
      }
      return C_PARSE_FLAGS[$0] ? '' : $0.slice(1, $0.length - 1)
    }
  })

  if (!isValid) return

  const todayDate = new Date()

  // TODO 按业务场景简单处理，后期还需完善
  const date = new Date(dateInfo.year || todayDate.getFullYear(), dateInfo.month || 0, dateInfo.day || 1, dateInfo.hour || 0, dateInfo.minute || 0, dateInfo.second || 0, dateInfo.millisecond || 0)

  return date
}

/**
 * 格式化当前日期对象
 * @param  {Date}   date   传入日期对象
 * @param  {String} format 格式化格式
 * @return {String}        格式化后的日期
 */
export const formatDate = (date = new Date(), format = 'yyyy-MM-dd') => {
  const o = {
    'M+': date.getMonth() + 1, // 月份
    'd+': date.getDate(), // 日
    'h+': date.getHours(), // 小时
    'm+': date.getMinutes(), // 分
    's+': date.getSeconds(), // 秒
    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
    S: date.getMilliseconds() // 毫秒
  }
  if (/(y+)/.test(format)) {
    format = format.replace(RegExp.$1, (`${date.getFullYear()}`).substr(4 - RegExp.$1.length))
  }
  for (const k in o) {
    if (new RegExp(`(${k})`).test(format)) {
      format = format.replace(RegExp.$1, (RegExp.$1.length === 1) ? o[k] : ((`00${o[k]}`).substr((`${o[k]}`).length)))
    }
  }

  return format
}

/**
 * 获取当前日期是周几
 * @param  {Date}  date 传入日期对象
 * @return {Number}     周几
 */
export const getDayOfWeek = date => {
  const temp = new Date(date.getTime())
  return temp.getDay()
}

/**
 * 往前移动N天
 * @param  {Date}  date    移动日期
 * @param  {Number} amount 移动天数
 * @return {Date}          所得日期
 */
export const prevDate = function(date, amount = 1) {
  return new Date(date.getFullYear(), date.getMonth(), date.getDate() - amount)
}

/**
 * 往后移动N天
 * @param  {Date}  date    移动日期
 * @param  {Number} amount 移动天数
 * @return {Date}          所得日期
 */
export const nextDate = function(date, amount = 1) {
  return new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount)
}

/**
 * 往前移动N月
 * @param  {Date}  date    移动日期
 * @param  {Number} amount 移动天数
 * @return {Date}          所得日期
 */
export const prevMonthAmount = function(date, amount = 1) {
  return new Date(date.getFullYear(), date.getMonth() - amount, date.getDate())
}

/**
 * 往后移动N月
 * @param  {Date}  date    移动日期
 * @param  {Number} amount 移动天数
 * @return {Date}          所得日期
 */
export const nextMonthAmount = function(date, amount = 1) {
  return new Date(date.getFullYear(), date.getMonth() + amount, date.getDate())
}

/**
 * 往前移动N年
 * @param  {Date} date 传入日期对象
 * @return {Date}      返回上一个年份日期对象
 */
export const prevYear = (date, amount = 1) => {
  const year = date.getFullYear()
  const month = date.getMonth()
  return changeYearMonthAndClampDate(date, year - amount, month)
}

/**
 * 往后移动N年
 * @param  {Date} date 传入日期对象
 * @return {Date}      返回下一个年份日期对象
 */
export const nextYear = (date, amount = 1) => {
  const year = date.getFullYear()
  const month = date.getMonth()
  return changeYearMonthAndClampDate(date, year + amount, month)
}

/**
 * 返回近一周时间区间对象
 * @return {Array<Date>} [起始时间，结束时间]
 */
export const getLastWeek = _ => {
  const date = new Date()
  let startTime, endTime
  const nowWeek = getDayOfWeek(date)
  if (nowWeek === 0) {
    startTime = prevDate(date, 6)
    endTime = date
  } else {
    startTime = prevDate(date, nowWeek - 1)
    endTime = nextDate(date, 7 - nowWeek)
  }

  return [startTime, endTime]
}

/**
 * 返回近一个月时间区间对象
 * @return {Array<Date>} [起始时间，结束时间]
 */
export const getLastMonth = _ => {
  const endTime = new Date()
  const startTime = prevMonthAmount(endTime)

  return [startTime, endTime]
}

/**
 * 返回下一个月时间区间对象
 * @return {Array<Date>} [起始时间，结束时间]
 */
export const getNextMonth = _ => {
  const startTime = new Date()
  const endTime = nextMonthAmount(startTime)

  return [startTime, endTime]
}

/**
 * 时间对象对比
 * @param  {string}  beginTime
 * @param  {string}  endTime
 * @return {boolean} endTime > beginTime 返回 true, 否则返回 false
 */
export const compareDate = (beginTime, endTime) => {
  return endTime.getTime() - beginTime.getTime() >= 0
}

/**
 * 时间对比【小时】
 * @param  {string}  beginTime
 * @param  {string}  endTime
 * @return {Boolean} endTime > beginTime 返回 true, 否则返回 false
 */
export const compareTime = (beginTime, endTime) => {
  return compareDate(new Date(`2010-10-10 ${beginTime}`), new Date(`2010-10-10 ${endTime}`))
}

/**
 * 时间对比【年月日】
 * @param  {string}  beginTime
 * @param  {string}  endTime
 * @return {Boolean} endTime > beginTime 返回 true, 否则返回 false
 */
export const compareDay = (beginTime, endTime) => {
  return compareDate(new Date(`${beginTime} 00:00:00`), new Date(`${endTime} 00:00:00`))
}

/**
 * 时间对比【全日期】
 * @param  {string}  beginTime
 * @param  {string}  endTime
 * @return {Boolean} endTime > beginTime 返回 true, 否则返回 false
 */
export const compareFullDate = (beginTime, endTime) => {
  return compareDate(new Date(beginTime), new Date(endTime))
}

/**
 * 时间对比，无视时间格式
 * @param  {string}  beginTime
 * @param  {string}  endTime
 * @return {boolean} endTime > beginTime 返回 true, 否则返回 false
 */
export const compare = (beginTime, endTime) => {
  if (beginTime instanceof Date) {
    beginTime = formatDate(beginTime, 'yyyy-MM-dd hh:mm:ss')
  }
  if (endTime instanceof Date) {
    endTime = formatDate(endTime, 'yyyy-MM-dd hh:mm:ss')
  }

  if (beginTime.length !== endTime.length) {
    throw new Error(`开始时间与结束时间长度不一致，无法对比：${beginTime}, ${endTime}`)
  }

  // 时分
  if (beginTime.length === 5 || beginTime.length === 8) {
    return compareTime(beginTime, endTime)
    // 年月日
  } else if (beginTime.length === 10) {
    return compareDay(beginTime, endTime)
    // 全日期
  } else if (beginTime.length === 19) {
    return compareFullDate(beginTime, endTime)
  }
  return false
}

/**
 * 获取当前月份总天数
 * @param  {Number} year  年份
 * @param  {Number} month 月份
 * @return {Number}       天数
 */
export const getDayCountOfMonth = (year, month) => {
  if (month === 3 || month === 5 || month === 8 || month === 10) {
    return 30
  }

  if (month === 1) {
    if (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)) {
      return 29
    } else {
      return 28
    }
  }

  return 31
}

/**
 * 根据年月日，生成传入日期精准小时数的日期对象
 * @param  {Date}   date 传入的日期独爱想
 * @param  {Number} y    年份
 * @param  {Number} m    月份
 * @param  {[type]} d    日
 * @return {Date}        返回与date相同小时的日期对象
 */
const modifyDate = function(date, y, m, d) {
  return new Date(y, m, d, date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds())
}

/**
 * 转化为日期对象，判断当前日是否超出当前月总天数，超出则设置为最后一天
 * @param  {Date}   date  传入日期对象
 * @param  {Number} year  年份
 * @param  {Number} month 月份
 * @return {Date}        转换后的日期对象
 */

export const changeYearMonthAndClampDate = function(date, year, month) {
  // clamp date to the number of days in `year`, `month`
  // eg: (2010-1-31, 2010, 2) => 2010-2-28
  const monthDate = Math.min(date.getDate(), getDayCountOfMonth(year, month))
  return modifyDate(date, year, month, monthDate)
}

/**
 * 返回当前月月初
 */
export const getNowMonthBegin = (date = new Date()) => {
  date.setDate(1)
  return date
}

/**
 * 返回当前月月末
 */
export const getNowMonthEnd = (date = new Date()) => {
  const days = getDayCountOfMonth(date.getFullYear(), date.getMonth())
  date.setDate(days)
  return date
}

/**
 * 返回时间长度
 */
export const formatTimeLength = time => {
  if (time === 0) {
    return 0
  }
  let str = ''
  const sec = time % 60
  if (sec !== 0) {
    str = `${sec}秒`
  }
  time -= sec
  if (time === 0) {
    return str
  }
  time = time / 60
  const min = time % 60
  if (min !== 0) {
    str = `${min}分${str}`
  }
  time -= min
  if (time === 0) {
    return str
  }
  time = time / 60
  const hour = time % 24
  if (hour !== 0) {
    str = `${hour}时${str}`
  }
  time -= hour
  if (time === 0) {
    return str
  }
  time = time / 24
  str = `${time}天${str}`
  return str
}
export const getDateString = (date = new Date(), format = 'yyyy-MM-dd') => {
  var o = {
    'M+': date.getMonth() + 1, // 月份
    'd+': date.getDate(), // 日
    'h+': date.getHours(), // 小时
    'm+': date.getMinutes(), // 分
    's+': date.getSeconds(), // 秒
    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
    'S': date.getMilliseconds() // 毫秒
  }
  if (/(y+)/.test(format)) {
    format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
  }
  for (var k in o) {
    if (new RegExp(`(${k})`).test(format)) {
      format = format.replace(RegExp.$1, (RegExp.$1.length === 1) ? o[k] : (('00' + o[k]).substr(('' + o[k]).length)))
    }
  }
  return format
}
/**
 * 获取上一个月份的日期
 * @param  {Date} date 传入日期对象
 * @return {Date}      返回上一个月日期对象
 */
export const prevMonth = date => {
  const year = date.getFullYear()
  const month = date.getMonth()
  return month === 0 ? changeYearMonthAndClampDate(date, year - 1, 11) : changeYearMonthAndClampDate(date, year, month - 1)
}
/**
 * 获取下一个月份的日期
 * @param  {Date} date 传入日期对象
 * @return {Date}      返回下一个月日期对象
 */
export const nextMonth = date => {
  const year = date.getFullYear()
  const month = date.getMonth()
  return month === 11 ? changeYearMonthAndClampDate(date, year + 1, 0) : changeYearMonthAndClampDate(date, year, month + 1)
}
/**
 * 时间对比，无视时间格式
 * @param  {string} time1
 * @param  {string} time2
 * @return {boolean}   time2>time1返回true,否则返回false
 */
export const timeCompare = (time1, time2) => {
  if (time1.length === 5) {
    if (time2.length === 5 && !timeCompareTime(time2, time1)) { return true }
    if (time2.length > 5 && timeSmallerTimeInterval(time2, time1)) { return true }
  } else {
    if (time2.length === 5 && timeBiggerTimeInterval(time1, time2)) { return true }
    if (time2.length > 5 && timeIntervalSmallerTimeInterval(time1, time2)) { return true }
  }
  return false
}
/**
 * 时间对比
 * @return {Boolean}              时间小于等于返回true,大于返回false
 */
export const timeCompareTime = (time1, time2) => {
  var timeStart = new Date('2010-10-10 ' + time1).getTime()
  var timeEnd = new Date('2010-10-10 ' + time2).getTime()
  return timeEnd - timeStart >= 0
}
/**
 * 时间小于时间段
 * @param  {string} timeInterval 时间段
 * @param  {string} time         时间点
 * @return {Boolean}
 */
export const timeSmallerTimeInterval = (timeInterval, time) => {
  const timeArr = timeInterval.split('-')
  const timeStart = timeArr[0]
  // 时间小于时间段
  if (!timeCompareTime(timeStart, time)) { return true }
  return false
}
/**
 * 时间大于时间段
 * @param  {string} timeInterval 时间段
 * @param  {string} time         时间点
 * @return {Boolean}
 */
export const timeBiggerTimeInterval = (timeInterval, time) => {
  const timeArr = timeInterval.split('-')
  const timeEnd = timeArr[1]
  // 时间大于时间段
  if (!timeCompareTime(time, timeEnd)) { return true }
  return false
}
/**
 * 时间段2大于时间段1
 * @param  {string} timeInterval1 时间段1
 * @param  {string} timeInterval2 时间段2
 * @return {boolean}              true为交集，false无交集
 */
export const timeIntervalSmallerTimeInterval = (timeInterval1, timeInterval2) => {
  const timeArr1 = timeInterval1.split('-')
  const endTime1 = timeArr1[1]
  const timeArr2 = timeInterval2.split('-')
  const startTime2 = timeArr2[0]
  if (!timeCompareTime(startTime2, endTime1)) { return true }
  return false
}
/**
 * 判断时间段，时间点是否交集
 * @param  {string} timeInterval 时间段
 * @param  {string} time         时间点
 * @return {Boolean}              true为交集，false无交集
 */
export const getTimeIntersection = (timeInterval, time) => {
  const timeArr = timeInterval.split('-')
  const timeStart = timeArr[0]
  const timeEnd = timeArr[1]
  // 区间范围外，无交集
  if (!timeCompareTime(timeStart, time) || !timeCompareTime(time, timeEnd)) { return false }
  return true
}
/**
 * 时间段对比
 * @param  {string} timeInterval1 时间段1
 * @param  {string} timeInterval2 时间段2
 * @return {boolean}              true为交集，false无交集
 */
export const timeIntervalCompareTimeInterval = (timeInterval1, timeInterval2) => {
  const timeArr1 = timeInterval1.split('-')
  const startTime1 = timeArr1[0]
  const endTime1 = timeArr1[1]
  const timeArr2 = timeInterval2.split('-')
  const startTime2 = timeArr2[0]
  const endTime2 = timeArr2[1]
  if (!timeCompareTime(startTime1, endTime2) || !timeCompareTime(startTime2, endTime1)) { return false }
  return true
}
